/******************************************************************
 *
 *    Java Lib For JavaWeb, Powered By Zhejiang hangzhou.
 *
 *    Copyright (c) 2001-2020
 *    http://tankpao.tk/
 *
 *    Package:     tk.tankpao.blast.web.servlet
 *
 *    Filename:    DispatcherServlet.java
 *
 *    Description: TODO(用一句话描述该文件做什么)
 *
 *    Copyright:   Copyright (c) 2001-2020
 *
 *    Company:     tankpao.tk
 *
 *    @author:     王志刚
 *
 *    @version:    1.0.0
 *
 *    Create at:   2017年2月9日 下午2:56:33
 *
 *    Revision:
 *
 *    2017年2月9日 下午2:56:33
 *        - first revision
 *
 *****************************************************************/
package tk.tankpao.blast.web.servlet;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper;

import tk.tankpao.blast.constant.ApplicationConstant;
import tk.tankpao.blast.core.ApplicationContext;
import tk.tankpao.blast.web.annotation.RequestMapping;
import tk.tankpao.blast.web.annotation.ResponseBody;
import tk.tankpao.blast.web.core.ClassUrlMapping;
import tk.tankpao.blast.web.util.RequestUtil;
import tk.tankpao.blast.web.util.spring.LocalVariableTableParameterNameDiscoverer;
import tk.tankpao.blast.web.util.spring.ParameterNameDiscoverer;

/**
 * 
 * @ClassName DispatcherServlet
 * @Description 请求分发器类
 *  * @author 王志刚
 * @Date 2017年2月9日 下午2:56:55
 * @version 1.0.0
 */
public class DispatcherServlet extends HttpServlet{
	
	private String PATH_PREFIX;
	private String PATH_SUFFIX;
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 5422369790295218149L;
	ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

	@Override
	public void init() throws ServletException {
		loadClassUrlMapping();
		PATH_PREFIX = ApplicationContext.getPps().getProperty(ApplicationConstant.VIEWS_BASE_PATH_PREFIX);
		PATH_SUFFIX = ApplicationContext.getPps().getProperty(ApplicationConstant.VIEWS_BASE_PATH_SUFFIX);
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void loadClassUrlMapping(){
		Map<String, Class> classeMap = ApplicationContext.getClasseMap();
		classeMap.forEach((key, clazz)->{
			RequestMapping rm = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
			if(rm != null){
				String basePath = rm.path();
				Method[] methods = clazz.getDeclaredMethods();
				Map<String, Object> mappingMap = null;
				for(Method method : methods){
					RequestMapping rmMethod = (RequestMapping)method.getAnnotation(RequestMapping.class);
					if(rmMethod != null){
						String subPath = rmMethod.path();
						String path = "/"+basePath+"/"+subPath;
						path = path.replaceAll("/+", "/").replaceAll("\\\\+", "/");
						mappingMap = new HashMap<String, Object>();
						mappingMap.put(ClassUrlMapping.CLASS_URL_MAPPING_PATH, path);
						mappingMap.put(ClassUrlMapping.CLASS_URL_MAPPING_TARGET, key);
						mappingMap.put(ClassUrlMapping.CLASS_URL_MAPPING_METHOD, method);
						ClassUrlMapping.getClassUrlMap().put(path, mappingMap);
					}
					
				}
			}
		});
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		ObjectMapper mapper = new ObjectMapper();
		String path = req.getRequestURI().replace(req.getContextPath(), "").replaceAll("/+", "/").replaceAll("\\\\+", "/");
		Map<String, Object> map = ClassUrlMapping.getClassUrlMap().get(path);
		if(map != null){
			try {
				Object target = ApplicationContext.getBean((String)map.get(ClassUrlMapping.CLASS_URL_MAPPING_TARGET));
				Method method = (Method) map.get(ClassUrlMapping.CLASS_URL_MAPPING_METHOD);
				String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
				Class<?>[] paraTypes = method.getParameterTypes();
				Object[] paraTargets = new Object[paraTypes.length];
				for(int i=0;i<paraTypes.length;i++){
					if(!RequestUtil.isBaseTypes(paraTypes[i])){
						paraTargets[i] = RequestUtil.queryStringToBean(paraTypes[i],req);
					}else{
						paraTargets[i] = RequestUtil.queryStringToBaseType(parameterNames[i], paraTypes[i], req);
					}
				}
				Object returnObj = method.invoke(target, paraTargets);
				
				if(method.isAnnotationPresent(ResponseBody.class)){
					 mapper.writeValue(resp.getOutputStream(), returnObj);
				}else{
					if(returnObj instanceof java.lang.String){
						String returnPath = String.valueOf(returnObj);
						req.getRequestDispatcher(PATH_PREFIX+returnPath+PATH_SUFFIX).forward(req, resp);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
}
